
from ..modbustcp import sysObjs
import json 
import socket
import threading
import time
from queue import Queue
import requests
class TInfluxDB:
    def __init__(self) -> None:
        self.host="meeting.xmgkfw.com"
        self.port = 42036
        self.token  = "Basic cm9vdDpoaHVjMTExNQ=="
        self.db = "pcs2023"
    def writeDatapoints(self , dataPoints ):
        try:
            headers = {
                "Authorization" : self.token,
                'Content-Type': 'application/json',
            }
            influxdb_url= "http://"+self.host+":"+str(self.port) + '/write?db='+self.db+'&rp=rePolicy_1h'
            resp = requests.post(url=influxdb_url , headers=headers , data=dataPoints)
            #print(resp)
        except Exception as er:
            print(er)

class TModbus:
    def __init__(self) -> None:
        self.devs = {}
        self.tasks = {}
        self.regs= {}
        self.vars ={}
        self.influxdbClient = TInfluxDB()
    def loadObjs(self , devs , tasks , regs , vars):
        try:
            for d in devs :
                dev = TMbDev()
                dev.devCode = d['devCode']
                dev.devHost = d['devHost']
                dev.devPort = d['devPort']
                dev.influxdbClient = self.influxdbClient
                dev.modbus = self
                self.devs[dev.devCode] = dev


            devKeys = self.devs.keys()
            for t in tasks:
                if t['devCode'] in devKeys:
                    task = TMbTask()
                    task.dev = self.devs[t['devCode']]
                    task.taskId = t['taskId']
                    task.unitId = t['unitId']
                    task.funCode = t['funCode']
                    task.regAddr = t['regAddr']
                    task.regNum = t['regNum']
                    self.tasks[task.taskId] = task
                    task.dev.tasks[task.taskId] = task
            taskKeys = self.tasks.keys()
            for r in regs:
                if r['taskId'] in taskKeys:
                    reg = TMbReg()
                    reg.task = self.tasks[r['taskId']]
                    reg.regKey = r['regKey']
                    reg.regAddr = r['regAddr'] 
                    self.regs[reg.regKey] = reg
                    reg.task.regs[reg.regKey] = reg
                    reg.task.dev.regs[reg.regKey] = reg
            regKeys = self.regs.keys()
            for v in vars:
                if v['regKey'] in regKeys:
                    var = TMbVar()
                    var.reg = self.regs[v['regKey']]
                    var.varId = v['varId']
                    var.regKey = v['regKey']
                    var.varType = v['varType']
                    var.bitFrom = v['bitFrom']
                    var.bitLen = v['bitLen']
                    var.varCaption = v['varCaption']
                    var.reg.vars[var.varId] = var
                    var.reg.task.dev.vars[var.varId]= var
                    self.vars[var.varId] = var
            print('load modbus objects.')                
        except Exception as er:
            print(er)
    def start(self):
        try:
            for dev in self.devs.values():
                dev.start()                
        except Exception as er:
            print(er)
    def log16(self, numArray):
        r = ''
        try:
            for i in range(len(numArray)):
                r = r + hex(numArray[i])+' '
        except Exception as er:
            print(er)
        return r
    def routerHandlerStart(self, result , request , router):
        try:
            self.start();
            result["status"] = 1;
            result["datas"] ={};
        except Exception as er:
            print(er)
    def routerHandlerWrite06(self, result , request , router):
        try:            
            ps = router.getRequestParams(request)
            if 'devCode' in ps.keys():
                devCode = ps['devCode'] 
                if devCode in self.devs.keys():
                    if not 'unitId' in ps.keys():
                        result["status"] = -2
                        result["error"] = "unitId is error" 
                    elif not 'regAddr' in ps.keys():
                        result["status"] = -3
                        result["error"] = "regAddr is error" 
                    elif not 'val' in ps.keys():
                        result["status"] = -4
                        result["error"] = "val is error" 
                    else:
                        dev = self.devs[devCode]
                        unitId = ps['unitId'] 
                        regAddr = ps['regAddr'] 
                        val = ps['val'] 
                        dev.writeRegister06(unitId,regAddr, val)
                        result["status"] = 1                    
                else:
                    result["status"] = -1
                    result["error"] = "devCode is error" 
            else:
                result["status"] = -1
                result["error"] = "devCode is error" 
        except Exception as er:
            print(er)
    def routerHandlerGetVars(self, result , request , router):
        res ={}
        try:   
            for dev in self.devs.values():
                res[dev.devCode] = dev.getVarVals()
            result["status"] = 1
            result["datas"] = res
        except Exception as er:
            print(er)
    def routerHandlerGetDevVars(self, result , request , router):
        try:            
            ps = router.getRequestParams(request)
            if 'devCode' in ps.keys():
                devCode = ps['devCode'] 
                if devCode in self.devs.keys():
                    dev = self.devs[devCode]
                    result["status"] = 1
                    result["datas"] = dev.getVarVals()
                else:
                    result["status"] = -1
                    result["error"] = "devCode is error" 
            else:
                result["status"] = -1
                result["error"] = "devCode is error" 
        except Exception as er:
            print(er)


class TMbDev:
    def __init__(self) -> None:
        self.modbus = None
        self.devCode = ""
        self.devHost = ""
        self.devPort = 502
        self.tasks = {}
        self.regs = {}
        self.vars = {}
        self.msgBuffer = {}

        self.msgId = 0
        self.skt = None
        self.influxdbClient = None
        self.threadSend = None
        self.threadRecv = None

        self.writeBuf = Queue(1000)

        self.decodeHandlers ={
            'V01': self.doDecode_v01 ,
            'V02': self.doDecode_v02 ,
            'V03': self.doDecode_v03 ,
            'V04': self.doDecode_v04 ,
            'V05': self.doDecode_v05 ,
            'V06': self.doDecode_v06 ,
            'V07': self.doDecode_v07 ,
            'V08': self.doDecode_v08 ,
        }
    def start(self):
        try:
            isConnected = self.isConnected()
            if not isConnected :
                self.connectHost()
                self.start()
            else:
                self.threadRecv = threading.Thread(target=self.threadExecRecv, daemon=True)
                self.threadSend = threading.Thread(target=self.threadExecSend, daemon=True)
                self.threadRecv.start()
                self.threadSend.start()
                
        except Exception as er:
            print(er)
    
    def isConnected(self):
        res = False
        try:
            if self.skt!=None and (not self.skt._closed):
                res = True
        except Exception as er:
            print(er)
        return res
    def getSendTaskBytes(self , task, msgId):
        res = []
        try:
            msgIdBytes = int.to_bytes(msgId , 2 , byteorder='big')
            regAddrBytes = int.to_bytes(task.regAddr , 2 , byteorder='big')
            regNumBytes = int.to_bytes(task.regNum , 2 , byteorder='big')
            funCode = task.funCode
            unitId = task.unitId
            res= [msgIdBytes[0],msgIdBytes[1],0,0,0,6, unitId ,funCode,regAddrBytes[0],regAddrBytes[1],regNumBytes[0],regNumBytes[1]]
        except Exception as er:
            print(er)
        return bytearray(res)
    def getVarVals(self ):
        res = []
        try:
            for var in self.vars.values():
                info = var.getInfo()
                res.append(info)
        except Exception as er:
            print(er)
        return res
    def doOnSetRegVal(self, reg):
        try:
            #if self.devCode =='pcs' :
            #print( self.devCode + ':'+ str( self.msgId))
            #print('set reg value '+reg.regKey+' - '+ str(reg.bit)+","+str(reg.h)+","+str(reg.l)+'  time:'+str(time.time()))

            for var in reg.vars.values():
                #print(str(var.varId)+':'+var.varType)
                tm = time.time()
                if var.varType in self.decodeHandlers.keys():
                    handler = self.decodeHandlers[var.varType]
                    val = handler(var)
                    if var.rate>0:
                        val = val * var.rate
                    self.doOnSetVarVal(var , val , tm)
                    #print('handler-'+str(var.varId)+ var.varType+str(val)+'--'+str(var.updateTime))

        except Exception as er:
            print(er)
    def doOnSetVarVal(self, var , val , updateTime):
        try:
            if var.val != val:                
                var.val = val
                nowTm = time.time()
                var.updateTime = nowTm                
                flag = False
                dltTime = nowTm - var.notifyTime
                if var.notifyTime==0 :
                    flag = True
                elif var.notifyType == 'N01':
                    flag = True
                elif var.notifyType =='N02':
                    if var.notifyVal == 0 :
                        flag = True
                    elif abs(var.notifyVal - val)*1.0/abs(var.notifyVal)> abs(var.notifyThreshold):
                        flag = True
                elif var.notifyType == 'N03' :
                    if abs(var.notifyVal - val)> var.notifyThreshold:
                        flag = True
                var.notifyFlag = flag
        except Exception as er:
            print(er)
    def doNotify(self):
        try:
            notifyVars ={}
            for var in self.vars.values():
                if var.notifyFlag:
                    notifyVars[var.varId] = var.val
            notifyJson = json.dumps(notifyVars)
            if len(notifyVars)>0:
                print('notify vars:'+ notifyJson)
                for var in self.vars.values():
                    if var.notifyFlag:
                        var.notifyVal = var.val 
                        var.notifyTime = var.updateTime 
                        var.notifyFlag = False

        except Exception as er:
            print(er)
    def connectHost(self):
        try:
            address = (self.devHost , self.devPort)
            self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.skt.settimeout(5)
            self.skt.connect(address) 
        except Exception as er:
            print(er)
        return self.isConnected()
    def writeRegister06(self, unitId , regAddr , val):
        try:
            res = []
            if self.msgId >= 0xfff0:
                self.msgId = 0
            self.msgId = self.msgId + 1
            msgIdBytes = int.to_bytes(self.msgId , 2 , byteorder='big')
            regAddrBytes = int.to_bytes(regAddr , 2 , byteorder='big')
            valBytes = int.to_bytes(val , 2 , byteorder='big')
            funCode = 0x06
            unitId = unitId
            res= [msgIdBytes[0],msgIdBytes[1],0,0,0,6, unitId ,funCode,regAddrBytes[0],regAddrBytes[1],valBytes[0],valBytes[1]]
            self.skt.send(bytearray(res))
        except Exception as er:
            print(er)
    def threadExecSend(self):
        try:
            sendIndex =0
            tasks = list(self.tasks.values())
            tasksCount = len(tasks)
            while ( 1>0):
                try:
                    time.sleep(0.2)
                    isConnected = self.isConnected()
                    if isConnected :
                        

                        if sendIndex< tasksCount:
                            if self.msgId >= 0xfff0:
                                self.msgId = 0
                            self.msgId = self.msgId + 1
                            task = tasks[sendIndex]
                            cmdBytes = self.getSendTaskBytes(task, self.msgId)
                            self.msgBuffer[self.msgId] = task
                            self.skt.send(cmdBytes)
                            sendIndex = sendIndex + 1
                        else:
                            sendIndex = 0 
                    else:
                        break
                except Exception as eer:
                    print(eer)
                    break;
            print("dev sendMsgService is stopped.")
        except Exception as er:
            print(er)
    def threadExecRecv(self):
        try:
            while 1>0 :
                isConnected = self.isConnected()
                if isConnected :
                    recHeaderBytes = self.skt.recv(6)
                    if len(recHeaderBytes)==6:
                        size = int.from_bytes(bytearray([recHeaderBytes[4],recHeaderBytes[5]]),byteorder='big')
                        recBodyBytes=None
                        c =0
                        while 1>0:
                            time.sleep(0.01)
                            recBodyBytes = self.skt.recv(size)
                            if len(recBodyBytes)==size:
                                break
                            elif c>=3:
                                break
                            else:
                                c = c+1 
                                time.sleep(0.01)
                        msgId = int.from_bytes(bytearray([recHeaderBytes[0],recHeaderBytes[1]]),byteorder='big')
                        if msgId in  self.msgBuffer.keys():
                            try:
                                task = self.msgBuffer.pop(msgId)
                                offset = 1
                                funCode = recBodyBytes[offset]
                                offset = offset + 1
                                if funCode == 1 or funCode == 2 :
                                    dataSize = recBodyBytes[offset]
                                    offset = offset + 1
                                    for i in range(dataSize):
                                        byteItem = recBodyBytes[offset]
                                        offset = offset + 1
                                        for k in range(8):
                                            val = (byteItem>>k) % 2 
                                            regAddr = task.regAddr + i*8 + k
                                            regKey = task.dev.devCode +'-'+ str(task.unitId)+'-'+str(task.funCode)+'-'+str(regAddr)
                                            if regKey in self.regs.keys():
                                                reg = self.regs[regKey]
                                                reg.bit = val     
                                                self.doOnSetRegVal(reg)                                       
                                elif funCode == 3 or funCode == 4 :
                                    dataSize = recBodyBytes[offset]
                                    regNum = int(0.5 * dataSize)
                                    offset = offset + 1
                                    for i in range(regNum):
                                        regAddr = task.regAddr + i
                                        regKey = task.dev.devCode +'-'+ str(task.unitId)+'-'+str(task.funCode)+'-'+str(regAddr)
                                        if regKey in self.regs.keys():
                                            reg = self.regs[regKey]
                                            reg.h = recBodyBytes[offset]
                                            offset = offset + 1
                                            reg.l = recBodyBytes[offset]
                                            offset = offset + 1
                                            self.doOnSetRegVal(reg)     
                                else:
                                    pass
                                self.write2InfluxDBv1x()
                                self.doNotify()
                            except Exception as eer:
                                print(eer)
                        else:
                            print('---------- write register ---------')
                            print(self.modbus.log16(recHeaderBytes))
                            print(self.modbus.log16(recBodyBytes))
                            print('---------- write register End---------')
                    else:
                        continue
                else:
                    break
            print("dev recvMsgService is stopped.")

        except Exception as er:
            print(er)
    def doDecode_v01(self , var):
        val = var.val
        try:
            bitPos = var.bitFrom 
            byteData = var.reg.l
            if bitPos>7:
                bitPos = bitPos-8
                byteData = var.reg.h
            val = (byteData>>bitPos) % 2
        except Exception as eer:
            print('decode v01 error.')
            print(eer)
        return val
    def doDecode_v02(self , var):
        val = var.val
        try:
            byteData = var.reg.l
            bitPos = var.bitFrom 
            if bitPos>=8:
                bitPos = bitPos - 8
                byteData = var.reg.h
            mask = ((1<<var.bitLen) -1 )<<bitPos
            val = byteData & mask
        except Exception as eer:
            print('decode v02 error.')
            print(eer) 
        return val
    def doDecode_v03(self , var):
        val = var.val
        try:
            val = int.from_bytes(bytearray([var.reg.h , var.reg.l]), byteorder='big', signed=True)
        except Exception as eer:
            print('decode v03 error.')
            print(eer)
        return val
    def doDecode_v04(self , var):
        val = var.val
        try:
            val = int.from_bytes(bytearray([var.reg.h , var.reg.l]), byteorder='big', signed=False)
        except Exception as eer:
            print('decode v04 error.')
            print(eer)
        return val
    def doDecode_v05(self , var):
        val = var.val
        try:
            regNext = var.reg.regKey.split('_')
            regAddr = var.reg.regAddr + 1
            nextRegKey = regNext[0]+"_"+regNext[1]+"_"+regNext[2]+"_"+str(regAddr)
            if nextRegKey in  self.regs.keys():
                nextReg = self.regs[nextRegKey]
                dataBytes =bytearray([var.reg.h,var.reg.l,nextReg.h, nextReg.l])
                val = int.from_bytes(dataBytes, byteorder='big', signed=False)
        except Exception as eer:
            print('decode v05 error.')
            print(eer)
        return val
    def doDecode_v06(self , var):
        val = var.val
        try:
            val = var.h
        except Exception as eer:
            print('decode v06 error.')
            print(eer)
        return val
    def doDecode_v07(self , var):
        val = var.val
        try:
            val = var.l
        except Exception as eer:
            print('decode v07 error.')
            print(eer)
        return val
    def doDecode_v08(self , var):
        val = var.val
        try:
            val = var.reg.bit
        except Exception as eer:
            print('decode v08 error.')
            print(eer)
        return val
    def getFullDataPoints(self):
        res = "t_";
        try:
            res = res +self.devCode+",flag=hhu v=0"
            for vk, vi in self.vars.items():
                res = res + ",v_"+str(vk)+"="+str(vi.val) 
        except Exception as er :
            print(er)
        return res
    def write2InfluxDBv1x(self):
        try:
            dataPoints = self.getFullDataPoints()
            self.influxdbClient.writeDatapoints(dataPoints)
        except Exception as er:
            print(er)
class TMbTask:
    def __init__(self) -> None:
        self.taskId = 0
        self.unitId = 0
        self.funCode = 0 
        self.regAddr = 0 
        self.regNum = 0 
        self.dev = None
        self.regs ={}
class TMbReg:
    def __init__(self) -> None:
        self.task = None
        self.regKey = ""
        self.regAddr = 0  
        self.bit = 0 
        self.h = 0 
        self.l = 0 
        self.updateTime = 0 
        self.vars={}
class TMbVar:
    def __init__(self) -> None:
        self.varId = 0
        self.regKey = ""
        self.reg = None
        self.varType = ''
        self.bitFrom = 0 
        self.bitLen = 0 
        self.val =  0 
        self.rate =  0 
        self.updateTime = 0  
        self.varCaption=""

        self.notifyType = 'N02'
        self.notifyThreshold = 0.05
        self.notifyTime = 0 
        self.notifyVal =0  
        self.notifyFlag = False
    def getInfo(self):
        res ={}
        try:
            res={                
                "varId": self.varId ,
                "regKey": self.reg.regKey,
                "regAddr": self.reg.regAddr,
                "val":self.val ,
                "updateTime":self.updateTime,
                "unitId": self.reg.task.unitId ,
                "devCode": self.reg.task.dev.devCode,
                "vName":self.varCaption
            }
        except Exception as er:
            print(er)
        return res

mb = TModbus()
mb.loadObjs(sysObjs.devs , sysObjs.tasks , sysObjs.regs , sysObjs.vars) 

###
# /api/modbus/start
# /api/modbus/getDevVars
# /api/modbus/getDevDevVars?devCode=pcs
###
#mb.devs['bms'].start()
#mb.devs['pcs'].start()
#mb.start()
#while 1>0 :
#    time.sleep(1)
